package io.tim.webserver.simple02;

/*
 * @author  Zhang Nan
 * @version 0.2
 * @since [28/02/2017]
 *
 */

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.net.BindException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;

import io.tim.webserver.simple02.util.MyFileReader;
import io.tim.webserver.simple02.util.Settings;
import io.tim.webserver.simple02.util.MyLogger;

class Processer implements Runnable {

	private String Server_Root = ""; // Web Server 配置的服务根目录
	private String Server_Home = ""; // Web Server 配置的欢迎首页
	private String Server_Icons_Path = ""; // Web Server 包含图标的路径
	// 定义一个将要用来接收和发送数据的 Socket 对象
	// 包含请求第一行信息的字符串，包括方法、文件名、协议版本，通过空格分割
	// 例如: GET /someFile HTTP/1.1
	private Socket socket;
	private String RequestFromClient = "";
	private String File_Name = ""; // 请求的文件名
	private String DIR_HTTP = ""; // 当前请求的子目录名
	private String NOM_HOTE = "";// 主机名
	private int position1 = 0;
	private int position2 = 0;

	private BufferedReader in;
	private PrintWriter out;

	/*
	 * Processor 构造器
	 * 
	 * @param s -- 服务器端建立起的 Socket 对象
	 * 
	 * @param Sroot -- Web Server 配置的服务根目录
	 * 
	 * @param Shome -- Web Server 配置的欢迎首页
	 * 
	 * @param Sicons -- Web Server 包含图标的路径
	 * 
	 * @param Slog -- Web Server 日志文件所在路径
	 * 
	 */
	public Processer(Socket s, String Sroot, String Shome, String Sicons, String Slog) throws UnknownHostException // 如果无法识别主机的名字或IP地址，就会抛出这种异常
			, IOException {

		try {
			// 属性初始化
			this.Server_Root = Sroot;
			this.Server_Home = Shome;
			this.Server_Icons_Path = Sicons;

			this.socket = s;

			// 从 Socket 的输入流中读取来自客户端的请求
			this.in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

			// 创建一个服务端的输出流，用于向客户端返回错误消息或成功请求的资源
			this.out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

			// 获取本地主机的 IP 地址
			NOM_HOTE = socket.getInetAddress().getLocalHost().toString();

			run(); // 调用线程的 run() 方法

		} catch (IndexOutOfBoundsException ie) {

			// 错误请求异常处理，记录日志
			MyLogger lg = new MyLogger();

			// 调用日志类 MyLogger 的 LogEvent 方法
			lg.LogEvent(Slog, "错误的请求", 2);

		}

	}

	/*
	 * DecodeURL 方法用来将 URL 中的 HTTP 编码 %20 处理成字符串空格 例如：/File%20Having%20space.htm
	 * = /File Having space.htm
	 */
	public String DecodeURL(String path) {

		String pathWithSpace = "";

		// 测试客户端请求的文件名中是否包含 %20
		if (path.indexOf("%20") == -1) {

			return path; // 如果不包含 %20 则原样返回
		}

		else {
			String[] fileTokens = path.split("%20");
			for (String s : fileTokens) {
				pathWithSpace += s + " ";
			}
			return pathWithSpace.substring(0, pathWithSpace.length() - 1);
		}
	}

	public synchronized void run() {

		try {
			InputStream is = socket.getInputStream();
			in = new BufferedReader(new InputStreamReader(is));
			out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(socket.getOutputStream())), true);

		} catch (IOException ioe) {
		}

		try {

			// 获取 Web Server 所在操作系统的文件分隔符
			String FILE_SEPERATOR = System.getProperty("file.separator");
			// 获取客户端发来的请求字符串
			RequestFromClient = in.readLine();
			if (RequestFromClient == null) {
				System.out.println("RequestFromClient is null and reset to the SERVER_ROOT... " + RequestFromClient);
				RequestFromClient = "GET / HTTP/1.1";
			}
			position1 = RequestFromClient.indexOf("/") + 1;
			position2 = RequestFromClient.indexOf("HTTP") - 1;

			DIR_HTTP = RequestFromClient.substring(position1, position2).trim();
			// 组装成服务器上的文件绝对路径，同时替换文件路径中的分隔符为系统分隔符
			File_Name = Server_Root + FILE_SEPERATOR + DIR_HTTP.replace("/", FILE_SEPERATOR);

			// 将文件名中的 "%20" 替换为空格
			File_Name = this.DecodeURL(File_Name);

			System.out.println("[客户端请求内容]: " + RequestFromClient);
			System.out.println("[被请求的文件绝对路径]: " + File_Name);
			System.out
					.println("*********************************************" + "**********************************\n");

			File f = new File(File_Name);
			MyFileReader myFileReader = new MyFileReader();

			myFileReader.setDirectory_Name(DIR_HTTP);

			// 检查客户请求的资源是否是一个目录
			if (f.isDirectory()) {
				System.out.println("请求的是一个目录...");
				if (new File(File_Name + Server_Home).exists()) {
					// 如果 Web Server 配置有默认的欢迎页面，则将该页面内容发送到客户端
					String welcome = File_Name + Server_Home;
					myFileReader.ReadFile(welcome, NOM_HOTE, socket.getOutputStream(), DIR_HTTP);
				} else {
					// 否则列出该目录下的所有内容显示给客户端
					myFileReader.ListDir(File_Name, socket.getOutputStream(), NOM_HOTE, Server_Icons_Path);
				}

			} else {
				// 如果请求的资源是一个文件，则将文件内容读取或者执行返回给客户端
				System.out.println("请求的是一个文件...");
				myFileReader.ReadFile(File_Name, NOM_HOTE, socket.getOutputStream(), DIR_HTTP);
			}

		} catch (IOException ioe) {

		} finally {
			try {
				socket.close(); // 最后都需要关闭 Socket
			} catch (IOException ioe) {
			}
		}
	}
}// End class Processor

public class WebServer {

	// 定义 Web Server 属性

	// Web Server 监听的端口号，并预先指定一个默认值
	private static int SERVER_PORT = 80;
	// Web Server 配置的服务根目录，并预先指定一个默认值
	private static String SERVER_ROOT = "D:\\tim\\webserver\\simple02\\htdocs";
	// Web Server 配置的欢迎页面，并预先指定一个默认值
	private static String SERVER_HOMEPAGE = "index.html";
	// Web Server 包含图标的路径，并预先指定一个默认值
	private static String SERVER_ICONS = "/icons/";
	// Web Server 记录日志的路径，并预先指定一个默认值
	private static String SERVER_LOG = "D:\\tim\\webserver\\simple02\\MyLogger\\MyLogger.txt";

	public void getServerDirectives() throws IOException {

		Settings set = new Settings();
		// 从配置文件中读取服务器属性值
		set.readConfFile("D:\\tim\\webserver\\simple02\\conf\\WebServer_Simple02.conf");

		this.SERVER_PORT = set.getServerPort();
		this.SERVER_ROOT = set.getServerRoot();
		this.SERVER_HOMEPAGE = set.getServerHomePage();
		this.SERVER_ICONS = set.getServerIcons();
		this.SERVER_LOG = set.getServerLog();

	}

	/**
	 * 收集系统信息
	 * 
	 */
	public void GeneralInfos() {

		// 获取操作系统用户信息
		String USER_HOME = System.getProperty("user.home");
		String USER_NAME = System.getProperty("user.name");
		// String USER_TIMEZONE = System.getProperty("user.timezone");

		// 获取操作系统信息
		String OS_NAME = System.getProperty("os.name");
		String OS_ARCH = System.getProperty("os.arch");
		String OS_VERS = System.getProperty("os.version");

		System.out.println("\nWebServer Simple02 \n");
		System.out.println("[操作系统]");
		System.out.println("\t|名称 =" + OS_NAME + "");
		System.out.println("\t|版本 =" + OS_VERS + "");
		System.out.println("\t|架构 =" + OS_ARCH + "");

		System.out.println("\n");

		System.out.println("[用户]");
		System.out.println("\t|名称 =" + USER_NAME + "");
		System.out.println("\t|目录 =" + USER_HOME + "");
		// System.out.println("\t|_TIMEZONE=" + USER_TIMEZONE + "");

		System.out.println("\n");

	}

	public static void main(String[] args) throws IOException {
		// 创建 Web Server 实例
		WebServer webServer = new WebServer();
		webServer.getServerDirectives(); // 读取配置进行初始化
		webServer.GeneralInfos(); // 显示系统及用户的信息

		try {
			// 创建一个新的 ServerSocket 并监听在配置的端口上
			ServerSocket S = new ServerSocket(SERVER_PORT);

			System.out.println("[系统状态]: Web Server 已启动...\n");
			try {
				while (true) {
					// 通过 ServerSocket 创建 Socket对象接收来自客户端的请求
					Socket sock = S.accept();

					try {
						System.out.println("[远程主机]: " + sock.getInetAddress().toString());
						System.out.println("[监听端口]: " + sock.getPort());

						// 实例化处理线程
						new Processer(sock, SERVER_ROOT, SERVER_HOMEPAGE, SERVER_ICONS, SERVER_LOG);

					} catch (IOException e) {
						sock.close();// 出现异常后关闭 Socket
					}
				}
			} finally {
				S.close(); // 最后要关闭 ServerSocket
			}
		} catch (BindException B) {
			// 处理当前已经存在正在运行的 WebServer 实例的异常
			System.out.println("WebServer 已经在运行，端口已被占用...");
			System.exit(0);
		}

	}
}// End class WebServer